<?php

/***
 * This constant is used quite heavily here, so let's make sure
 * it's properly defined:
 ***/

if(false == defined('DIRECTORY_SEPARATOR'))
{
	define('DIRECTORY_SEPARATOR', (PHP_OS == 'WIN32' || PHP_OS == 'WINNT' ? chr(92) : chr(47)));
}


/***
 * Class BootLoaderClass is responsible for initial system load and bootstraping. Can be used
 * as a standalone library or as a full blow suite of applications.
 ***/
class BootLoaderClass {

   // ! Constructor Method

   /**
	* Instantiates class and defines instance variables.
	*
	* @param Boolean $boot_minimum If True Cogsworth will load only it's core.
	* @param String $path By specifying a path, you can run this framework from any location your server.
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access public
	* @return Void
	*/
	public function __construct($boot_minimum = false, $path = '')
	{
		define('MIN_BOOT', $boot_minimum);
		define('PATH_SYSTEM', $path ? $path : getcwd());
		define('PHP_EXT', '.php');
	}


   // ! Executor

   /**
	* Gathers important library files, initializes and
	* executes the chosen event.
	*
	* @param None
	* @author Daniel Wilhelm II Murdoch <wilhelm.murdoch@gmail.com>
	* @since v 1.0.0
	* @access public
	* @return Void
	*/
	public function initSystem()
	{
		/***
		 * Load essential core group of constants:
		 ***/

		require_once PATH_SYSTEM . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'constants' . PHP_EXT;


		/***
		 * Load up the benchmarking and observer classes
		 * before anything else:
		 ***/

		require_once PATH_COGSWORTH . 'observer.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'benchmark.core' . PHP_EXT;


		CW_ObserverClass::notify('begin');


		/***
		 * Get the error reporting suite loaded up:
		 ***/

		require_once PATH_COGSWORTH . 'exception.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'log.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'error.core' . PHP_EXT;


		if(DEV_MODE)
		{
			error_reporting(E_ALL);

			if(class_exists('ErrorClass'))
			{
				set_error_handler('CogsworthErrorHandler');
			}
		}


		CW_ObserverClass::notify('load_error_reporting');


		/***
		 * Let's load up the bare necessities:
		 ***/

		CW_ObserverClass::notify('pre_load_core_libraries');

		require_once PATH_COGSWORTH . 'application.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'url.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'controller.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'model.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'registry.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'xsl.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'xml.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'mount.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'config.core' . PHP_EXT;
		require_once PATH_COGSWORTH . 'input.core' . PHP_EXT;

		CW_ObserverClass::notify('post_load_core_libraries');


		/***
		 * Alright, let's enclose this in a try / catch construct and
		 * catch any exceptions thrown by our libraries:
		 ***/

		try
		{
			/***
			 * Load the main system configuration file:
			 ***/

			CW_ObserverClass::notify('pre_load_config');

			$Config = new CW_ConfigClass;

			$Config->load(PATH_SYSTEM . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php', 'system');

			CW_ObserverClass::notify('post_load_config');


			/***
			 * Instantiate the object registry and begin adding some
			 * essential libraries:
			 ***/

			CW_ObserverClass::notify('pre_registry_load');

			$Reg = new CW_RegistryClass;

			$Reg->registerObject('Config', $Config);
			$Reg->registerObject('Xsl', new CW_XslClass);
			$Reg->registerObject('Xml', new XmlElement(XML_ROOT_TAG));
			$Reg->registerObject('Mount', new CW_MountClass($Reg));
			$Reg->registerObject('Input', new CW_InputClass);
			$Reg->registerObject('App', new CW_ApplicationClass($Reg));

			CW_ObserverClass::notify('post_registry_load');


			/***
			 * Load all user input into our request handling class:
			 ***/

			CW_ObserverClass::notify('pre_input_load');

			$Reg->Input->addStack('get', $_GET, false);
			$Reg->Input->addStack('post', $_POST, false);

			CW_ObserverClass::notify('post_input_load');


			/***
			 * If we're doing a full system boot up, then start loading and
			 * executing the requested application and controller.If not,
			 * then just return TRUE and be done with it.
			 ***/

			if(false == MIN_BOOT)
			{
				/***
				 * Fist, let's determine what application to load and then what controller
				 * we need to fire up. We do this by parsing the URL query string:
				 ***/

				CW_ObserverClass::notify('pre_schema_load');

				$Reg->registerObject('Url', new CW_UrlSchemaClass($Reg));

				$Reg->Url->prepUrlSchema($Reg->Input->get('schema', '', 'get'));

				define('CURRENT_APPLICATION', $Reg->App->setCurrentApplication($Reg->Url->searchForApplication()));

				CW_ObserverClass::notify('post_schema_load');


				/***
				 * Now let's declare some application-specific paths:
				 ***/

				CW_ObserverClass::notify('pre_app_constants');

				define('PATH_APPLICATION', PATH_APPS . CURRENT_APPLICATION . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_CONFIG', PATH_APPLICATION . 'config' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_CONTROLLERS', PATH_APPLICATION . 'controllers' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_CORE', PATH_APPLICATION . 'core' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_LOCALE', PATH_APPLICATION . 'locale' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_MODELS', PATH_APPLICATION . 'models' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_VIEWS', PATH_APPLICATION . 'views' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_FACTORIES', PATH_APPLICATION . 'factories' . DIRECTORY_SEPARATOR);
				define('PATH_APPLICATION_CACHE', PATH_APPLICATION . 'cache' . DIRECTORY_SEPARATOR);


				/***
				 * Let us not forget declaring convenient paths to common design elements:
				 ***/

				define('PATH_DESIGN_CSS', PATH_APPLICATION_VIEWS . 'css' . DIRECTORY_SEPARATOR);
				define('PATH_DESIGN_IMAGES', PATH_APPLICATION_VIEWS . 'images' . DIRECTORY_SEPARATOR);
				define('PATH_DESIGN_JS', PATH_APPLICATION_VIEWS . 'js' . DIRECTORY_SEPARATOR);
				define('PATH_DESIGN_XSL', PATH_APPLICATION_VIEWS . 'xsl' . DIRECTORY_SEPARATOR);

				CW_ObserverClass::notify('post_app_constants');


				/***
				 * Next, we load up the selected applications settings and store them with
				 * the system's settings:
				 ***/

				CW_ObserverClass::notify('pre_load_app_config');

				$Reg->Config->load(PATH_APPLICATION_CONFIG . 'config.php', CURRENT_APPLICATION);

				$Reg->App->setDefaultController($Reg->Config->get('system', 'default_controller', CURRENT_APPLICATION));
				$Reg->Url->setController($Reg->Config->get('system', 'default_controller', CURRENT_APPLICATION));

				define('CURRENT_CONTROLLER', $Reg->App->setCurrentController(CURRENT_APPLICATION, $Reg->Url->searchForController()));
				define('NAMESPACE', $Reg->Config->get('system', 'namespace', CURRENT_APPLICATION));
				define('CURRENT_CONTROLLER_CLASS', NAMESPACE . ucwords(strtolower(CURRENT_CONTROLLER)) . 'ControllerClass');

				CW_ObserverClass::notify('post_load_app_config');


				/***
				 * Does the application have it's own cookie settings?
				 ***/

				if($Reg->Config->get('cookie', 'enable', CURRENT_APPLICATION))
				{
					CW_ObserverClass::notify('pre_load_cookies');

					require_once PATH_CORE . 'cookie.core' . PHP_EXT;

					$Reg->Input->addStack('cookie', $_COOKIE, false);

					$Reg->registerObject('Cookie', new CW_CookieClass($Reg->Input->getStack('cookie')));

					$Reg->Cookie->setCookiePath($Reg->Config->get('cookie', 'cookie_path', CURRENT_APPLICATION));
					$Reg->Cookie->setCookieDomain($Reg->Config->get('cookie', 'cookie_domain', CURRENT_APPLICATION));
					$Reg->Cookie->setCookiePrefix($Reg->Config->get('cookie', 'cookie_prefix', CURRENT_APPLICATION));

					CW_ObserverClass::notify('post_load_cookies');
				}


				/***
				 * Does this application use a database? If so, create an instance of the
				 * selected database type and connect.
				 ***/

				if($Reg->Config->get('database', 'enable', CURRENT_APPLICATION))
				{
					CW_ObserverClass::notify('pre_load_db');

					$driver = $Reg->Config->get('database', 'driver', CURRENT_APPLICATION);

					if(false == file_exists(PATH_DATABASE . "{$driver}.driver" . PHP_EXT))
					{
						throw new CoreException("The database driver '{$driver}' could not be located in '" . PATH_DATABASE . "'.");
					}

					require_once PATH_DATABASE . 'driver.interface' . PHP_EXT;
					require_once PATH_DATABASE . "{$driver}.driver" . PHP_EXT;

					$Reg->registerObject('DB', new CW_DatabaseDriver($Reg));

					if(false == $Reg->DB->connect($Reg->Config->get('database', 'host', CURRENT_APPLICATION),
												  $Reg->Config->get('database', 'port', CURRENT_APPLICATION),
												  $Reg->Config->get('database', 'user', CURRENT_APPLICATION),
												  $Reg->Config->get('database', 'password', CURRENT_APPLICATION),
												  $Reg->Config->get('database', 'schema', CURRENT_APPLICATION)))
					{
						throw new CoreException("A database connection for application '" . CURRENT_APPLICATION . "' could not be established!");
					}

					define('DB_PREFIX', $Reg->Config->get('database', 'prefix', CURRENT_APPLICATION));

					CW_ObserverClass::notify('post_load_db');
				}


				/***
				 * Instantiate the selected controller and wrap it within the application's
				 * boot loader. We use the boot loader as a wrapper so you can execute logic
				 * before the controller is actually executed. This allows you encapsulate
				 * this functionality within the namespace of your application:
				 ***/

				CW_ObserverClass::notify('pre_app_boot');

				if(false == file_exists(PATH_APPLICATION . 'boot' . PHP_EXT))
				{
					throw new CoreException("The bootstrap file for application '" . CURRENT_APPLICATION . "' could not be found!");
				}

				require_once PATH_APPLICATION . 'boot' . PHP_EXT;
				require_once PATH_APPLICATION_CONFIG . 'build' . PHP_EXT;
				require_once PATH_APPLICATION_CONTROLLERS . CURRENT_CONTROLLER . '.controller' . PHP_EXT;

				$controller_class_name = CURRENT_CONTROLLER_CLASS;

				$ApplicationBootLoader = new CW_ApplicationBootLoaderClass($Reg, new $controller_class_name($Reg));

				CW_ObserverClass::notify('post_app_boot');


				/***
				 * Does this application use locale packs? Build the list of language definitions:
				 ***/

				if($Reg->Config->get('locale', 'enable', CURRENT_APPLICATION))
				{
					CW_ObserverClass::notify('pre_locale_load');

					require_once PATH_CORE . 'locale.core' . PHP_EXT;

					$Reg->registerObject('Locale', new CW_LocaleClass($Reg->Config->get('locale', 'default', CURRENT_APPLICATION)));

					$Reg->Locale->setPathToLocaleDirectory(PATH_APPLICATION_LOCALE);

					$Reg->Locale->loadDefinitions('global.locale' . PHP_EXT);
					$Reg->Locale->loadDefinitions(CURRENT_CONTROLLER . '.locale' . PHP_EXT);

					CW_ObserverClass::notify('post_locale_load');
				}


				/***
				 * Finally, let's execute the selected controller:
				 ***/

				CW_ObserverClass::notify('pre_app_init');

				$controller_output = $ApplicationBootLoader->initApplication();

				CW_ObserverClass::notify('pre_app_init');


				/***
				 * Does this controller have any XSL templates loaded? If so, begin
				 * building the XSL document:
				 ***/

				if($Reg->Xsl->useXsl())
				{
					/***
					 * Add some default template variables:
					 ***/

					CW_ObserverClass::notify('pre_xsl_variable_load');

					$Reg->Xsl->addVariable('std_current_controller', CURRENT_CONTROLLER);
					$Reg->Xsl->addVariable('std_application', CURRENT_APPLICATION);
					$Reg->Xsl->addVariable('std_framework', FRAMEWORK);
					$Reg->Xsl->addVariable('std_build', BUILD);
					$Reg->Xsl->addVariable('std_current_time', time());


					/***
					 * Add some path information:
					 ***/

					$Reg->Xsl->addVariable('path_url', $Reg->Config->get('system', 'url', CURRENT_APPLICATION));
					$Reg->Xsl->addVariable('path_images', $Reg->Config->get('system', 'url', CURRENT_APPLICATION) . 'apps/' . CURRENT_APPLICATION . '/views/images');
					$Reg->Xsl->addVariable('path_css', $Reg->Config->get('system', 'url', CURRENT_APPLICATION) . 'apps/' . CURRENT_APPLICATION . '/views/css');
					$Reg->Xsl->addVariable('path_javascript', $Reg->Config->get('system', 'url', CURRENT_APPLICATION) . 'apps/' . CURRENT_APPLICATION . '/views/js');


					/***
					 * Pass through our query string parameters:
					 ***/

					foreach(array_merge($Reg->Input->getStack('post'), $Reg->Input->getStack('get')) as $key => $val)
					{
						$Reg->Xsl->addVariable("request_{$key}", $val);
					}

					CW_ObserverClass::notify('post_xsl_variable_load');


					/***
					 * Since we're using templates, we might as well pass through our language
					 * definitions into our XML document:
					 ***/

					CW_ObserverClass::notify('pre_xml_flush');

					$XmlWrapper = new XmlElement('root', '', array('framework' => FRAMEWORK, 'build' => BUILD, 'application' => CURRENT_APPLICATION, 'controller' => CURRENT_CONTROLLER));
					$XmlWrapper->addNode($Reg->Xml);

					$xml_output = $XmlWrapper->returnXmlBuffer(true, true);

					CW_ObserverClass::notify('post_xml_flush');


					/***
					 * Is 'debug' attached to the URL? If so, display the debugger:
					 ***/

					if($Reg->Input->exists('debug', 'get'))
					{
						if(false == class_exists('CW_Common'))
						{
							require_once PATH_COGSWORTH . 'common.core' . PHP_EXT;
						}

						CW_ObserverClass::notify('pre_debug_view');

						require_once PATH_COGSWORTH . 'debug.core' . PHP_EXT;

						$Debug = new CW_Debug($Reg);

						$Debug->setXml($xml_output);
						$Debug->setXsl($Reg->Xsl->buildXslOutput());
						$Debug->setHtml($Reg->Xsl->transform($xml_output));

						CW_ObserverClass::notify('post_debug_view');

						die($Debug->showDebugScreen($Reg->Input->get('debug', 'xml')));
					}


					CW_ObserverClass::notify('pre_xsl_transform');

					$controller_output = $Reg->Xsl->transform($xml_output);

					CW_ObserverClass::notify('post_xsl_transform');
				}
			}


			/***
			 * Close the database connection if one is active:
			 ***/

			if($Reg->Config->get('database', 'enable', CURRENT_APPLICATION))
			{
				CW_ObserverClass::notify('pre_db_close');

				$Reg->DB->close();

				CW_ObserverClass::notify('post_db_close');
			}


			/***
			 * Return output:
			 ***/

			return $controller_output;
		}
		catch(CoreException $Exception)
		{
			ErrorClass::add($Exception->get());
		}
	}
}

?>